package streams

import (
	"encoding/json"
	"errors"
	"gitee.com/hongzhaomin/hzm-common-go/assert"
	"gitee.com/hongzhaomin/hzm-common-go/easylog"
	"gitee.com/hongzhaomin/hzm-common-go/strutil"
	"slices"
	"sort"
	"strings"
)

// Of 将T类型切片转化为T类型的Stream对象，支持单个传入
func Of[T any](ele ...T) Stream[T] {
	return OfSlice(ele)
}

// OfSlice 将T类型切片转化为T类型的Stream对象
func OfSlice[T any](list []T) Stream[T] {
	// 不影响原来的切片，新建一个
	sList := make([]T, 0, len(list))
	sList = append(sList, list...)
	newStream := func(l any) any {
		switch arr := l.(type) {
		case []string:
			s := new(strStream)
			s.list = arr
			s.child = s
			return s
		case []int:
			s := new(numberStream[int])
			s.list = arr
			s.child = s
			return s
		case []int64:
			s := new(numberStream[int64])
			s.list = arr
			s.child = s
			return s
		case []float64:
			s := new(numberStream[float64])
			s.list = arr
			s.child = s
			return s
		default:
			s := new(stream[T])
			s.list = sList
			return s
		}
	}(sList)
	return newStream.(Stream[T])
}

// Map 函数，将T类型的切片，转换为R类型的Stream对象
func Map[T any, R any](ts []T, t2r func(t T) R) Stream[R] {
	assert.NonNil(t2r, "t2r func must not nil")
	rs := make([]R, 0, len(ts))
	for _, t := range ts {
		rs = append(rs, t2r(t))
	}
	return OfSlice(rs)
}

// MapStream 函数，将T类型的Stream对象，转换为R类型的Stream对象
func MapStream[T any, R any](st Stream[T], t2r func(t T) R) Stream[R] {
	return Map(st.ToSlice(), t2r)
}

// Stream 定义 Stream[T] 接口
type Stream[T any] interface {

	// MapIdentity 将集合中T类型t转为一个新的T类型数据
	MapIdentity(convert func(t T) T) Stream[T]

	// Peek 执行eachDo函数，对t做操作会影响原始值，某种意义上来说和 MapIdentity 功能类似
	Peek(eachDo func(t *T)) Stream[T]

	// Filter 判断t是否需要过滤
	// filter 函数返回true表示保留t，返回false表示剔除t
	Filter(filter func(t T) bool) Stream[T]

	// Skip 跳过limit个元素
	Skip(limit int) Stream[T]

	// SkipIndex 从index下标开始，跳过limit个元素
	SkipIndex(index int, limit int) Stream[T]

	// Distinct 集合去重方法
	Distinct() Stream[T]

	// Sort 升序排序，如果是不可比较类型，会将T转为json字符串作为比较类型
	Sort() Stream[T]

	// SortFunc 可以取T中的某些属性进行排序
	// 调用示例：
	//
	//	slices.SortFunc(my.list, func(a, b T) int {
	//		return cmp.Or(
	//			cmp.Compare(a.Customer, b.Customer),
	//			cmp.Compare(a.Product, b.Product),
	//			cmp.Compare(b.Price, a.Price),
	//		)
	//	})
	SortFunc(cmp func(a, b T) int) Stream[T]

	// Reverse 将当前集合倒叙排序
	Reverse() Stream[T]

	// MapToInt 将T类型的Stream转为NumberStream[int]类型的Stream
	MapToInt(toIntFun func(t T) int) NumberStream[int]

	// MapToLong 将T类型的Stream转为NumberStream[int64]类型的Stream
	MapToLong(toLongFun func(t T) int64) NumberStream[int64]

	// MapToDouble 将T类型的Stream转为NumberStream[float64]类型的Stream
	MapToDouble(toDoubleFun func(t T) float64) NumberStream[float64]

	// MapToString 将T类型的Stream转为StrStream类型的Stream
	MapToString(toStrFun func(t T) string) StrStream

	// FindFirst 返回集合第一个元素的指针，空集合则返回nil
	FindFirst() *T

	// FindFirstOrElse 返回集合第一个元素，空集合则返回默认值ele
	// ele - T类型默认值
	FindFirstOrElse(ele T) T

	// FindFirstOrElseGet 返回集合第一个元素，空集合则执行get函数返回
	// get - 获取T类型默认值的函数
	FindFirstOrElseGet(get func() T) T

	// ToSlice 返回T类型的切片
	ToSlice() []T

	// ForEach 遍历集合，调用eachDo函数执行操作
	ForEach(eachDo func(t T))

	// Count 返回集合数量count
	Count() int

	// ToMapStrKey 将集合转换为map[string]any, 根据getKey函数指定 “string类型” 的key，根据getVal函数指定value
	// getKey - 根据t获取key的函数
	// getVal - 根据t获取value的函数
	ToMapStrKey(getKey func(t T) string, getVal func(t T) any) map[string]any

	// ToMapIntKey 将集合转换为map[int]any, 根据getKey函数指定 “int类型” 的key，根据getVal函数指定value
	// getKey - 根据t获取key的函数
	// getVal - 根据t获取value的函数
	ToMapIntKey(getKey func(t T) int, getVal func(t T) any) map[int]any

	// ToMapLongKey 将集合转换为map[int64]any, 根据getKey函数指定 “int64类型” 的key，根据getVal函数指定value
	// getKey - 根据t获取key的函数
	// getVal - 根据t获取value的函数
	ToMapLongKey(getKey func(t T) int64, getVal func(t T) any) map[int64]any

	// ToMapIdentityStrKey 将集合转换为map[string]T, 根据getKey函数指定 “string类型” 的key
	// getKey - 根据t获取key的函数
	ToMapIdentityStrKey(getKey func(t T) string) map[string]T

	// ToMapIdentityIntKey 将集合转换为map[int]T, 根据getKey函数指定 “int类型” 的key
	// getKey - 根据t获取key的函数
	ToMapIdentityIntKey(getKey func(t T) int) map[int]T

	// ToMapIdentityLongKey 将集合转换为map[int64]T, 根据getKey函数指定 “int64类型” 的key
	// getKey - 根据t获取key的函数
	ToMapIdentityLongKey(getKey func(t T) int64) map[int64]T

	// GroupingByIdentityStrKey 将集合根据key分组转换为map[string][]T, 根据getKey函数指定 “string类型” 的key
	// getKey - 根据t获取key的函数
	GroupingByIdentityStrKey(getKey func(t T) string) map[string][]T

	// GroupingByIdentityIntKey 将集合根据key分组转换为map[int][]T, 根据getKey函数指定 “int类型” 的key
	// getKey - 根据t获取key的函数
	GroupingByIdentityIntKey(getKey func(t T) int) map[int][]T

	// GroupingByIdentityLongKey 将集合根据key分组转换为map[int64][]T, 根据getKey函数指定 “int64类型” 的key
	// getKey - 根据t获取key的函数
	GroupingByIdentityLongKey(getKey func(t T) int64) map[int64][]T

	// AnyMatch 遍历集合，任意一个元素使得predicate函数返回true, 则返回true
	// 即：predicate(a) || predicate(b) ...
	AnyMatch(predicate func(t T) bool) bool

	// AllMatch 遍历集合，所有元素使得predicate函数返回true, 则返回true
	// 即：predicate(a) && predicate(b) ...
	AllMatch(predicate func(t T) bool) bool

	// NoneMatch 遍历集合，所有元素使得predicate函数返回false, 则返回true
	// 即：!(predicate(a) || predicate(b) ...)
	NoneMatch(predicate func(t T) bool) bool

	// Join 根据delimiter分隔符将集合中的所有元素拼接成一个字符串返回
	Join(delimiter string) string

	// Join2 根据delimiter分隔符将集合中的所有元素拼接成一个字符串，然后拼接一个prefix前缀和一个suffix后缀返回
	Join2(delimiter, prefix, suffix string) string
}

var _ Stream[any] = (*stream[any])(nil)

// stream: 定义Stream接口实现类
type stream[T any] struct {
	// 需要操作的切片数据
	list []T

	// Stream对象的子对象
	// 当从子对象转换父对象时，调用子方法就调用不了了
	// 所有加一个子对象，方便Stream对象转换成父类时，实现调用子对象方法
	child Stream[T]
}

func (my *stream[T]) MapIdentity(convert func(t T) T) Stream[T] {
	assert.NonNil(convert, "convert func must not nil")
	for i, e := range my.list {
		my.list[i] = convert(e)
	}
	return my
}

func (my *stream[T]) Peek(eachDo func(t *T)) Stream[T] {
	assert.NonNil(eachDo, "eachDo func must not nil")
	// 使用for-i的目的是为了让action函数能够修改元素
	// for-range 中的元素值是一个临时局部变量，不会影响到切片中的值
	for i := 0; i < len(my.list); i++ {
		eachDo(&my.list[i])
	}
	return my
}

func (my *stream[T]) Filter(filter func(t T) bool) Stream[T] {
	assert.NonNil(filter, "filter func must not nil")
	list := my.list
	if len(list) > 0 {
		r, w := 0, 0
		for ; r < len(list); r++ {
			if filter(list[r]) {
				// 进入判断，则保留
				list[w] = list[r]
				w++
			}
		}
		my.list = list[:w]
	}
	return my
}

func (my *stream[T]) Skip(limit int) Stream[T] {
	return my.SkipIndex(0, limit)
}

func (my *stream[T]) SkipIndex(index int, limit int) Stream[T] {
	if limit <= 0 {
		panic(errors.New("limit 必须大于0"))
	}
	if index < 0 || index > len(my.list)-1 {
		panic(errors.New("索引越界"))
	}
	if index+limit > len(my.list) {
		panic(errors.New("元素个数越界"))
	}
	// my.list[0:index] 表示从0下标开始取，取index长度的数据（左闭右开）
	my.list = append(my.list[0:index], my.list[index+limit:]...)
	return my
}

// Distinct 集合去重方法
func (my *stream[T]) Distinct() Stream[T] {
	if my.child == nil {
		list := my.list

		// 转成map, key: toJson value: T
		json2TMap := make(map[string]T, len(list))
		for _, t := range list {
			jsonKey := toJson(t)
			json2TMap[jsonKey] = t
		}

		// 再将map的value转成list
		newList := make([]T, 0, len(list))
		for _, val := range json2TMap {
			newList = append(newList, val)
		}

		my.list = newList
		return my
	}
	my.child.Distinct()
	return my
}

func (my *stream[T]) Sort() Stream[T] {
	if my.child == nil {
		sort.Sort(my)
		return my
	}
	my.child.Sort()
	return my
}

// SortFunc 可以取T中的某些属性进行排序
// 调用示例：
//
//	slices.SortFunc(my.list, func(a, b T) int {
//			return cmp.Or(
//				cmp.Compare(a.Customer, b.Customer),
//				cmp.Compare(a.Product, b.Product),
//				cmp.Compare(b.Price, a.Price),
//			)
//	})
func (my *stream[T]) SortFunc(cmp func(a, b T) int) Stream[T] {
	slices.SortFunc(my.list, cmp)
	return my
}

func (my *stream[T]) Reverse() Stream[T] {
	slices.Reverse(my.list)
	return my
}

func (my *stream[T]) MapToInt(toIntFun func(t T) int) NumberStream[int] {
	assert.NonNil(toIntFun, "toIntFun func must not nil")
	ns := Map(my.list, toIntFun).(*numberStream[int])
	if ns.child == nil {
		ns.child = ns
	}
	return ns
}

func (my *stream[T]) MapToLong(toLongFun func(t T) int64) NumberStream[int64] {
	assert.NonNil(toLongFun, "toLongFun func must not nil")
	ns := Map(my.list, toLongFun).(*numberStream[int64])
	if ns.child == nil {
		ns.child = ns
	}
	return ns
}

func (my *stream[T]) MapToDouble(toDoubleFun func(t T) float64) NumberStream[float64] {
	assert.NonNil(toDoubleFun, "toDoubleFun func must not nil")
	ns := Map(my.list, toDoubleFun).(*numberStream[float64])
	if ns.child == nil {
		ns.child = ns
	}
	return ns
}

func (my *stream[T]) MapToString(toStrFun func(t T) string) StrStream {
	assert.NonNil(toStrFun, "toStrFun func must not nil")
	ss := Map(my.list, toStrFun).(*strStream)
	if ss.child == nil {
		ss.child = ss
	}
	return ss
}

func (my *stream[T]) FindFirst() *T {
	if len(my.list) > 0 {
		return &my.list[0]
	}
	return nil
}

func (my *stream[T]) FindFirstOrElse(ele T) T {
	if len(my.list) > 0 {
		return my.list[0]
	}
	return ele
}

func (my *stream[T]) FindFirstOrElseGet(get func() T) T {
	if len(my.list) > 0 {
		return my.list[0]
	}
	return get()
}

func (my *stream[T]) ToSlice() []T {
	return my.list
}

func (my *stream[T]) ForEach(action func(t T)) {
	assert.NonNil(action, "action func must not nil")
	for _, t := range my.list {
		action(t)
	}
}

//func (my *stream[T]) ForEachPtr(action func(t *T)) {
//	assert.NonNil(action, "action func must not nil")
//	// 使用for-i的目的是为了让action函数能够修改元素
//	// for-range 中的元素值是一个临时局部变量，不会影响到切片中的值
//	for i := 0; i < len(my.list); i++ {
//		action(&my.list[i])
//	}
//}

func (my *stream[T]) Count() int {
	return len(my.list)
}

func (my *stream[T]) ToMapStrKey(getKey func(t T) string, getVal func(t T) any) map[string]any {
	return ToMap(my.list, getKey, getVal)
}

func (my *stream[T]) ToMapIntKey(getKey func(t T) int, getVal func(t T) any) map[int]any {
	return ToMap(my.list, getKey, getVal)
}

func (my *stream[T]) ToMapLongKey(getKey func(t T) int64, getVal func(t T) any) map[int64]any {
	return ToMap(my.list, getKey, getVal)
}

func (my *stream[T]) ToMapIdentityStrKey(getKey func(t T) string) map[string]T {
	return ToMapIdentity(my.list, getKey)
}

func (my *stream[T]) ToMapIdentityIntKey(getKey func(t T) int) map[int]T {
	return ToMapIdentity(my.list, getKey)
}

func (my *stream[T]) ToMapIdentityLongKey(getKey func(t T) int64) map[int64]T {
	return ToMapIdentity(my.list, getKey)
}

func (my *stream[T]) GroupingByIdentityStrKey(getKey func(t T) string) map[string][]T {
	return GroupingByIdentity(my.list, getKey)
}

func (my *stream[T]) GroupingByIdentityIntKey(getKey func(t T) int) map[int][]T {
	return GroupingByIdentity(my.list, getKey)
}

func (my *stream[T]) GroupingByIdentityLongKey(getKey func(t T) int64) map[int64][]T {
	return GroupingByIdentity(my.list, getKey)
}

func (my *stream[T]) AnyMatch(predicate func(t T) bool) bool {
	assert.NonNil(predicate, "predicate func must not nil")
	for _, t := range my.list {
		if predicate(t) {
			return true
		}
	}
	return false
}

func (my *stream[T]) AllMatch(predicate func(t T) bool) bool {
	assert.NonNil(predicate, "predicate func must not nil")
	for _, t := range my.list {
		if !predicate(t) {
			return false
		}
	}
	return true
}

func (my *stream[T]) NoneMatch(predicate func(t T) bool) bool {
	assert.NonNil(predicate, "predicate func must not nil")
	for _, t := range my.list {
		if predicate(t) {
			return false
		}
	}
	return true
}

func (my *stream[T]) Join(delimiter string) string {
	return my.Join2(delimiter, "", "")
}

func (my *stream[T]) Join2(delimiter, prefix, suffix string) string {
	if my.child == nil {
		jsonList := make([]string, 0, len(my.list))
		for _, l := range my.list {
			jsonList = append(jsonList, toJson(l))
		}
		return prefix + strings.Join(jsonList, ",") + suffix
	}
	return my.child.Join2(delimiter, prefix, suffix)
}

// ========================================  以下实现排序接口 sort.Interface ========================================

func (my *stream[T]) Len() int           { return len(my.list) }
func (my *stream[T]) Less(i, j int) bool { return toJson(my.list[i]) < toJson(my.list[j]) }
func (my *stream[T]) Swap(i, j int)      { my.list[i], my.list[j] = my.list[j], my.list[i] }

// ========================================  以下是工具函数 ========================================
func toJson(obj any) string {
	jsonObj, err := json.Marshal(obj)
	if err != nil {
		easylog.Error("json转换失败，%v", err)
		return strutil.Empty
	}
	return string(jsonObj)
}
